package com.acegear.horizon.domain.services;

import com.acegear.horizon.domain.models.ModelFactory;
import com.acegear.horizon.domain.models.constraint.RecommendType;
import com.acegear.horizon.domain.models.jpa.*;
import com.acegear.horizon.domain.models.vo.FollowVO;
import com.acegear.horizon.domain.repository.jpa.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Created by wangsike on 2016/12/12.
 */
@Service
public class UserService {

    @Autowired
    private PasswordAuthRepository passwordAuthRepository;

    @Autowired
    private WeiboAuthRepository weiboAuthRepository;

    @Autowired
    private WechatAuthRepository wechatAuthRepository;

    @Autowired
    private UserAuthRepository userAuthRepository;

    @Autowired
    private UserPhoneRepository userPhoneRepository;

    @Autowired
    private UserEmailRepository userEmailRepository;

    @Autowired
    private EmailVerifyRepository emailVerifyRepository;

    @Autowired
    private PhoneVerifyRepository phoneVerifyRepository;

    @Autowired
    private SecureTokenRepository secureTokenRepository;

    @Autowired
    private UserInfoRepository userInfoRepository;

    @Autowired
    private UserFollowRepository userFollowRepository;

    @Autowired
    private ModelFactory modelFactory;

    @Autowired
    private SMSService smsService;

    @Autowired
    private MailService mailService;

    @Autowired
    private RecommendRepository recommendRepository;


    @Autowired
    private FollowService followService;

    @Autowired
    private DefaultSettingsRepository defaultSettingsRepository;

    private DefaultSettings defaultSettings;

    public UserService() {

    }


    @Transactional
    public Boolean checkUserToken(Long userId, String accessToken) {
        return userAuthRepository.findByUserId(userId)
                .map(userAuth -> userAuth.checkToken(accessToken)).orElse(false);
    }

    public Boolean isFollowed(Long followeeId, Long followerId) {
        return userFollowRepository.findTopByFolloweeIdAndFollowerId(followeeId, followerId).isPresent();
    }

    public Boolean userIdExist(Long userId) {
        return userAuthRepository.findByUserId(userId).isPresent();
    }

    public Boolean usernameExist(String username) {
        return passwordAuthRepository.findByUsername(username).isPresent();
    }

    public Boolean uidExist(String uid) {
        return weiboAuthRepository.findByUid(uid).isPresent();
    }

    public Boolean unionIdExist(String unionId, Integer type) {
        return wechatAuthRepository.findByUnionIdAndType(unionId, type).isPresent();
    }

    public Boolean unionIdExist(String unionId) {
        return wechatAuthRepository.findTopByUnionId(unionId).isPresent();
    }

    public Boolean openIdExist(String openId) {
        return wechatAuthRepository.findByOpenId(openId).isPresent();
    }

    public Boolean phoneExist(String phone) {
        return userPhoneRepository.findByPhone(phone).isPresent();
    }

    public Boolean emailExist(String email) {
        return userEmailRepository.findByEmail(email).isPresent();
    }

    @Transactional
    public Boolean requestPhoneVerify(String phone) {
        PhoneVerify phoneVerify = new PhoneVerify(phone);
        phoneVerifyRepository.save(phoneVerify);
        return smsService.sendVerifySMS(phoneVerify);
    }

    @Transactional
    public Boolean requestEmailVerify(String email) {
        EmailVerify emailVerify = new EmailVerify(email);
        emailVerifyRepository.save(emailVerify);
        return mailService.sendVerifyMail(emailVerify);
    }

    @Transactional
    public Optional<String> verifyPhone(String phone, String code) {
        return phoneVerifyRepository
                .findByPhoneAndCode(phone, code)
                .filter(phoneVerify -> phoneVerify.verifyCode(code))
                .map(phoneVerify -> {
                    SecureToken secureToken = SecureToken.secureTokenByPhone(phone);
                    return secureTokenRepository.save(secureToken).getToken();
                });
    }

    @Transactional
    public Optional<String> verifyEmail(String email, String code) {
        return emailVerifyRepository
                .findByEmailAndCode(email, code)
                .filter(emailVerify -> emailVerify.verifyCode(code))
                .map(emailVerify -> {
                    SecureToken secureToken = SecureToken.secureTokenByEmail(email);
                    return secureTokenRepository.save(secureToken).getToken();
                });
    }

    @Transactional
    public Boolean checkSecureToken(String token, String str) {
        return secureTokenRepository
                .findByToken(token)
                .map(secureToken -> {
                    Boolean r = secureToken.verify(str);
                    secureTokenRepository.save(secureToken);
                    return r;
                })
                .orElse(false);
    }

    public Optional<WechatAuth> getWechatAuth(Long userId, Integer type) {
        return wechatAuthRepository.findByUserIdAndType(userId, type);
    }

    public Optional<WeiboAuth> getWeiboAuth(Long userId) {
        return weiboAuthRepository.findByUserId(userId);
    }


    public List<FollowVO> getRecommends(Pageable pageable, Long viewerId) {
        return recommendRepository
                .findByType(RecommendType.USER, pageable)
                .stream()
                .map(Recommend::getExternalId)
                .map(externalId -> followService.getFollowVO(externalId, viewerId))
                .collect(Collectors.toList());
    }


    public DefaultSettings getDefault() {
        if (defaultSettings == null) {
            defaultSettings = defaultSettingsRepository.findOne(1l);
            if (defaultSettings == null) {
                defaultSettings = new DefaultSettings();
            }
        }
        return defaultSettings;
    }
}
